#ifndef __CPU_H__
#define __CPU_H__

#include <types.h>
#include <multiboot.h>

// from start.asm!
extern char *gdt;
extern char *idt;

extern uint32_t gdtable;
extern uint32_t idtable;

extern void i386lgdt(uint32_t);
extern void i386lidt(uint32_t);

/* sels in GDT table */
#define SEL_KCODE	8
#define SEL_KDATA	16
#define	SEL_UCODE	(24|3)
#define SEL_UDATA	(32|3)
#define SEL_KTSS	40

#define EFLAGS_TF		0x100
#define EFLAGS_IF		0x200
#define EFLAGS_IOPL0	0x0000
#define EFLAGS_IOPL1	0x1000
#define EFLAGS_IOPL2	0x2000
#define EFLAGS_IOPL3	0x3000
#define EFLAGS_VM		0x20000

#define EFLAGS_IF0	(EFLAGS_IF|EFLAGS_IOPL0)
#define EFLAGS_IF3	(EFLAGS_IF|EFLAGS_IOPL3)
#define EFLAGS_TF0	(EFLAGS_TF|EFLAGS_IOPL0)
#define EFLAGS_TF3	(EFLAGS_TF|EFLAGS_IOPL3)

/* rights bits options */
#define i386rPRESENT 0x80	/* segment is present */

#define i386rDPL0    0x00
#define i386rDPL1    0x20
#define i386rDPL2    0x40
#define i386rDPL3    0x60

/* for Data/Code/TSS segments */
#define i386rDATA    0x12	/* segment is data, read/write */
#define i386rDATAro  0x10	/* segment is data, read only */
#define i386rCODE    0x1A	/* segment is code, read/exec */
#define i386rCODExo  0x18	/* segment is code, read only */

#define i386rTSS     0x09	/* segment is an i386 TSS */
#define i386rTSSbusy 0x0B
#define i386rLDT     0x02
#define i386rCGATE   0x0C

/* gran bits options */
#define i386g4K      0x80	/* segment gran is 4K (else 1B) */
#define i386g32BIT   0x40	/* segment default is 32bit (else 16bit) */
#define i386gAVL     0x10	/* segment AVL is set */

struct tss_def {
    uint32_t back_link;
    uint32_t esp0;
    uint32_t ss0;
    uint32_t esp1;
    uint32_t ss1;
    uint32_t esp2;
    uint32_t ss2;
    uint32_t cr3;
    uint32_t eip;
    uint32_t eflags;

    uint32_t eax;
    uint32_t ecx;
    uint32_t edx;
    uint32_t ebx;
    uint32_t esp;
    uint32_t ebp;
    uint32_t esi;
    uint32_t edi;

    uint32_t es;
    uint32_t cs;
    uint32_t ss;
    uint32_t ds;
    uint32_t fs;
    uint32_t gs;

    uint32_t ldtr;
    uint16_t flags;
    uint16_t io_bitmap_offset;
};

#define save_flags(x) \
__asm__ __volatile__("pushfl ; popl %0":"=r" (x): /* no input */ :"memory")

#define restore_flags(x) \
__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"r" (x):"memory")

#define halt() \
__asm__ __volatile__("cli ; hlt": :)

#define EXCEPTION_HANDLER(func) void func (unsigned int errcode);\
 asm(".globl " #func " \n " #func ": popl " #func  "_errcode \n "\
 "popl " #func "_address \n pushl " #func "_address \n pusha \n pushl " #func "_errcode \n"\
 "pushl " #func "_address \n call _" #func " \n addl $8, %esp \n popa \n iret \n "\
 #func "_errcode: .long 0 \n" #func "_address: .long 0");\
 void _ ## func(unsigned int address, unsigned int errcode)

#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((uint32_t)(x),(ptr),sizeof(*(ptr))))

struct __xchg_dummy {
    unsigned long a[100];
};
#define __xg(x) ((struct __xchg_dummy *)(x))

static inline uint32_t __xchg(uint32_t x, void *ptr, int size)
{
    switch (size) {
    case 1:
      __asm__("xchgb %b0,%1":"=q"(x)
      :	"m"(*__xg(ptr)), "0"(x)
      :	"memory");
	break;
    case 2:
      __asm__("xchgw %w0,%1":"=r"(x)
      :	"m"(*__xg(ptr)), "0"(x)
      :	"memory");
	break;
    case 4:
      __asm__("xchgl %0,%1":"=r"(x)
      :	"m"(*__xg(ptr)), "0"(x)
      :	"memory");
	break;
    }

    return (x);
}

extern void splx(int);
extern int splhi(void);

void i386set_seg(void *entry,
		 uint32_t base, uint32_t limit, uchar rights, uchar gran);
void i386set_taskgate(void *entry, uint16_t selector, uchar rights);
void i386ltr(uint16_t selector);
void systables_init();
void irq_enable(void);
void irq_disable(void);
void add_idtirq(uint32_t * IDT, uint32_t num, uint32_t sel, void *func);
void add_idtexc(uint32_t * IDT, uint32_t num, uint32_t sel, void *func);
void add_idtuirq(uint32_t * IDT, uint32_t num, uint32_t sel, void *func);
void idt_init();
void gdt_init();
void systss_init();
int arch_init(multiboot_info_t *, unsigned);

#endif	/* __CPU_H__ */
